
package com.hlkj.pay.app.merchant.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;

import org.redisson.api.RLock;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.hlkj.framework.caches.service.RedisTemplateService;
import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.util.collection.CollectionUtils;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.framework.extension.executor.ExtensionExecutor;
import com.hlkj.pay.app.merchant.ext.MerchantExtConstant;
import com.hlkj.pay.app.order.impl.OrderNotifyHandler;
import com.hlkj.pay.common.constants.MerchantConstant;
import com.hlkj.pay.common.constants.MqConstant;
import com.hlkj.pay.config.ExtRocketMQTemplate;
import com.hlkj.pay.dto.merchant.MerchantAccessDto;
import com.hlkj.pay.dto.merchant.MerchantAuthStatusConfig;
import com.hlkj.pay.dto.merchant.ProviderChannelFeeConfigList;
import com.hlkj.pay.dto.merchant.access.MerchantAccessConsumerDto;
import com.hlkj.pay.enums.CommonEnum;
import com.hlkj.pay.enums.MerchantEnum;
import com.hlkj.pay.enums.PayOrderEnums;
import com.hlkj.pay.gateway.merchant.IMerchantAccessGateway;
import com.hlkj.pay.infrastructure.model.merchant.MerchantAccessAuthDO;
import com.hlkj.pay.infrastructure.model.merchant.MerchantAccessInfoDO;
import com.hlkj.pay.infrastructure.model.merchant.MerchantConfigDO;
import com.hlkj.pay.infrastructure.model.pay.ProviderChannelConfigDO;
import com.hlkj.pay.service.common.IOperatedLogService;
import com.hlkj.pay.service.merchant.IMerchantAccessAuthService;
import com.hlkj.pay.service.merchant.IMerchantAccessService;
import com.hlkj.pay.service.merchant.IMerchantConfigService;
import com.hlkj.pay.service.pay.IPayChannelService;
import com.hlkj.pay.service.pay.IProviderService;
import com.hlkj.pay.util.DateUtils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author HlpayTeam
 * @date 2024/09/25 13:09
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MerchantAccessStatusProcessHandler {

    private final IMerchantAccessService merchantAccessService;

    private final IMerchantAccessAuthService merchantAccessAuthService;

    private final IOperatedLogService merchantAccessLogService;

    private final IMerchantConfigService merchantConfigService;

    private final IPayChannelService payChannelService;

    private final IProviderService providerService;

    private final ExtensionExecutor extensionExecutor;

    private final ExtRocketMQTemplate extRocketMQTemplate;

    private final OrderNotifyHandler orderNotifyHandler;

    private final MerchantAccessChannelService merchantAccessChannelService;

    private final RedisTemplateService redisTemplateService;

    private final IMerchantAccessGateway merchantAccessGateway;

    @Bean(name = "merchantAccessStatusProcessTask")
    public Executor merchantAccessStatusProcessTask() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5); // 核心线程数
        executor.setMaxPoolSize(10); // 最大线程数
        executor.setQueueCapacity(1000); // 队列容量
        executor.setKeepAliveSeconds(60); // 线程空闲时间
        executor.setThreadNamePrefix("Async-merchantAccess"); // 线程名称前缀
        executor.initialize();
        return executor;
    }


    @Async("merchantAccessStatusProcessTask")
    public void processAuthAccess(MerchantAuthStatusConfig merchantAuthStatusConfig) {
        MerchantAccessDto merchantAccessDto = merchantAccessService.domainDetail(merchantAuthStatusConfig.getAccessId());
        if (merchantAccessDto == null) {
            log.warn("processAuthAccess 数据不存在 id: {}", merchantAuthStatusConfig.getAccessId());
            return;
        }
        CommonResult<Integer> commonResult = merchantAccessGateway.queryMerAuthStatus(merchantAccessDto,merchantAuthStatusConfig);
        if(commonResult.isSuccess()){
            Integer authStatus = commonResult.getData();
            if(authStatus != null){
                // 根据报备号查询数据
                MerchantAccessAuthDO authDO = new MerchantAccessAuthDO();
                authDO.setAccessId(merchantAuthStatusConfig.getAccessId());
                authDO.setSubMchId(merchantAuthStatusConfig.getReportMerchantNo());
                MerchantAccessAuthDO merchantAccessAuthDO =  merchantAccessAuthService.queryMerchantAccessAuth(authDO);
                if(merchantAccessAuthDO != null){
                    authDO.setId(merchantAccessAuthDO.getId());
                    authDO.setAuthStatus(authStatus);
                    merchantAccessAuthService.updateMerchantAccessAuth(authDO);
                }
            }
        }
    }


    @Async("merchantAccessStatusProcessTask")
    public void processAccess(long id) {
        MerchantAccessConsumerDto merchantAccessConsumerDto = new MerchantAccessConsumerDto();
        merchantAccessConsumerDto.setAccessId(id);
        processAccess(merchantAccessConsumerDto, null);
    }

    public void processAccessMessage(MerchantAccessConsumerDto merchantAccessConsumerDto) {
        log.info("收到进件消息处理:{}", JsonUtils.toJsonStringNotNull(merchantAccessConsumerDto));
        processAccess(merchantAccessConsumerDto, null);
    }

    public void processAccess(MerchantAccessConsumerDto merchantAccessConsumerDto, Integer merchantAccessQuestType) {
        Long id = merchantAccessConsumerDto.getAccessId();
        log.info("processAccessMessage id:{}", id);
        MerchantAccessDto merchantAccessDto = merchantAccessService.domainDetail(id);
        if (merchantAccessDto == null) {
            log.warn("processAccessMessage 数据不存在 id: {}", id);
            return;
        }
        MerchantAccessInfoDO merchantAccessInfoDO = merchantAccessDto.getMerchantAccessInfo();
        MerchantEnum.ACCESS_STATUS accessStatus = MerchantEnum.ACCESS_STATUS.from(merchantAccessInfoDO.getStatus());
        MerchantEnum.CHANNEL_CONFIG_STATUS channelConfigStatus = MerchantEnum.CHANNEL_CONFIG_STATUS.from(merchantAccessInfoDO.getAuditStatus());
        log.info("当前需要accessStatus:{},channelConfigStatus:{}", accessStatus, channelConfigStatus);
        MerchantEnum.MERCHANT_ACCESS_QUEST_TYPE accessQuestType;
        if (merchantAccessQuestType != null) {
            accessQuestType = MerchantEnum.MERCHANT_ACCESS_QUEST_TYPE.from(merchantAccessQuestType);
        }
        else {
            accessQuestType = MerchantEnum.MERCHANT_ACCESS_QUEST_TYPE.from(accessStatus, channelConfigStatus);
        }
        if (accessQuestType == null) {
            if (channelConfigStatus == null) {
                log.info("当前无需进行状态处理,当前进件状态为 id:{},accessStatus:{}", id, accessStatus.getDesc());
            }
            else {
                log.info("当前无需进行状态处理,当前进件状态为 id:{},accessStatus:{},channelConfigStatus:{}", id, accessStatus.getDesc(), channelConfigStatus.getDesc());
            }
            return;
        }
        else {
            log.info("当前需要进行状态处理,当前进件状态为 id:{},accessQuestType:{}", id, accessQuestType.getDesc());
        }
        // String requestUrl =
        // extensionExecutor.execute(ChannelMerchantAccessExtPt.class,
        // BizScenario.valueOf(merchantAccessInfoDO.getChannelCode()),
        // ex -> ex.requestUrl(merchantAccessDto));
        // merchantAccessDto.setPayChannelRequest(payChannelService.queryRequestByAccessId(id,
        // requestUrl));
        ProviderChannelConfigDO providerChannelConfigDO = providerService.queryConfig(merchantAccessInfoDO.getProviderCode(), merchantAccessInfoDO.getChannelCode());
        MerchantConfigDO merchantConfigDO = merchantConfigService.queryConfig(merchantAccessInfoDO.getSn());

        MerchantAccessInfoDO updateMerchantAccessInfoDO = new MerchantAccessInfoDO();
        updateMerchantAccessInfoDO.setId(id);
        if (merchantConfigDO == null) {
            updateMerchantAccessInfoDO.setStatus(MerchantEnum.ACCESS_STATUS.APPROVE_REFUSE.getCode());
            updateMerchantAccessInfoDO.setRemark("商户配置为空");
            merchantAccessService.updateAccessInfo(updateMerchantAccessInfoDO, null);
            log.error("当前需要进行状态处理,商户配置为空 id:{},sn:{}", id, merchantAccessInfoDO.getSn());
            return;
        }
        List<ProviderChannelFeeConfigList> merchantFeeConfigList = merchantConfigDO.getMerchantFeeConfigList();
        if (CollectionUtils.isEmpty(merchantFeeConfigList)) {
            updateMerchantAccessInfoDO.setStatus(MerchantEnum.ACCESS_STATUS.APPROVE_REFUSE.getCode());
            updateMerchantAccessInfoDO.setRemark("商户费率配置为空");
            merchantAccessService.updateAccessInfo(updateMerchantAccessInfoDO, null);
            log.error("当前需要进行状态处理,商户费率配置为空 id:{},sn:{}", id, merchantAccessInfoDO.getSn());
            return;
        }
        if (providerChannelConfigDO == null || !StringUtils.hasText(providerChannelConfigDO.getParamsConfig())) {
            updateMerchantAccessInfoDO.setStatus(MerchantEnum.ACCESS_STATUS.APPROVE_REFUSE.getCode());
            updateMerchantAccessInfoDO.setRemark("通道参数配置为空");
            merchantAccessService.updateAccessInfo(updateMerchantAccessInfoDO, null);
            log.error("当前需要进行状态处理,通道参数配置为空 id:{},sn:{}", id, merchantAccessInfoDO.getSn());
            return;
        }
        boolean extensionExist = merchantAccessChannelService.extensionExist(merchantAccessInfoDO.getChannelCode());
        if (!extensionExist) {
            updateMerchantAccessInfoDO.setStatus(MerchantEnum.ACCESS_STATUS.APPROVE_REFUSE.getCode());
            updateMerchantAccessInfoDO.setRemark("当前通道进件未实现,请切换其他通道");
            merchantAccessService.updateAccessInfo(updateMerchantAccessInfoDO, null);
            log.error("当前需要进行状态处理,商户配置为空 id:{},sn:{}", id, merchantAccessInfoDO.getSn());
            return;
        }
        boolean process = true;
        String key = MerchantConstant.REDIS_MERCHANT_ACCESS_STATUS + id;
        RLock rLock = redisTemplateService.getLock(key);
        if (!rLock.tryLock()) {
            // sendMerchantAccessDelayMessage(merchantAccessConsumerDto);
            log.info("当前进件任务处理中，请等待 id:{},sn:{}", id, merchantAccessInfoDO.getSn());
            return;
        }
        try {
            switch (accessQuestType) {
                case APPLY:
                    merchantAccessChannelService.processApply(merchantAccessDto);
                    break;
                case SIGN:
                    merchantAccessChannelService.processSign(merchantAccessDto);
                    break;
                case SIGN_QUERY:
                    merchantAccessChannelService.processSignQuery(merchantAccessDto);
                    break;
                case BUSINESS_CONFIG:
                    boolean businessConfig = true;
                    if (merchantAccessInfoDO.getMerchantAccessType() != null && CommonEnum.YES_NO_TYPE.YES.getCode().equals(merchantAccessInfoDO.getMerchantAccessType())) {
                        if (merchantAccessInfoDO.getAuditStatus() > MerchantEnum.CHANNEL_CONFIG_STATUS.OPEN_PRODUCT.getCode()) {
                            businessConfig = false;
                        }
                    }

                    if (businessConfig) {
                        process = merchantAccessChannelService.businessConfig(merchantAccessDto);
                    }
                    else {
                        updateMerchantAccessInfoDO.setStatus(MerchantEnum.ACCESS_STATUS.APPROVE_SUCCESS.getCode());
                        merchantAccessService.updateAccessInfo(updateMerchantAccessInfoDO, null);
                        log.error("当前商户入网类型 为分账子商户 直接结束进件流程 id:{},sn:{}", id, merchantAccessInfoDO.getSn());
                        return;
                    }
                    break;
                case WECHAT_CONFIG:
                    merchantAccessChannelService.wechatConfig(merchantAccessDto);
                    break;
                case WECHAT_CONFIG_QUERY:
                    merchantAccessChannelService.wechatConfigQuery(merchantAccessDto, true, null);
                    break;
                case WECHAT_AUTH_CONFIG:
                    merchantAccessChannelService.wechatAuthConfig(merchantAccessDto);
                    break;
                case WECHAT_AUTH_CONFIG_QUERY:
                    merchantAccessChannelService.wechatAuthConfigQuery(merchantAccessDto, true, null);
                    break;
                case ALIPAY_AUTH_CONFIG:
                    merchantAccessChannelService.aliAuthConfig(merchantAccessDto);
                    break;
                case ALIPAY_AUTH_CONFIG_QUERY:
                    merchantAccessChannelService.aliAuthConfigQuery(merchantAccessDto, true, null);
                    break;
                case APPLY_QUERY:
                    merchantAccessChannelService.applyQuery(merchantAccessDto);
                    List<String> continueList = new ArrayList<>();
                    continueList.add(MerchantExtConstant.CHANNEL_WX_PAY);
                    continueList.add(MerchantExtConstant.CHANNEL_YEE_PAY);
                    continueList.add(MerchantExtConstant.CHANNEL_LESHUA_PAY);
                    continueList.add(MerchantExtConstant.CHANNEL_HAIKE_PAY);
                    // 微信进件查询后 不需要继续走下面流程
                    if(CollUtil.contains(continueList,merchantAccessInfoDO.getChannelCode()) && merchantAccessInfoDO.getStatus() != MerchantEnum.ACCESS_STATUS.APPROVE_REFUSE.getCode()){
                        sendMerchantAccessDelayMessage(merchantAccessConsumerDto);
                        return;
                    }
                    break;
                case SYSTEM_AUTH:
                case SYSTEM_APPROVE:
                    merchantAccessChannelService.systemAuth(merchantAccessDto);
                    break;
                case SYNC_CHANNEL:
                    merchantAccessChannelService.syncChannel(merchantAccessDto);
                    // merchantAccessChannelService.systemAuth(merchantAccessDto);
                    notifyChannel(merchantAccessDto);
                    process = false;
                    break;
                case APPROVE_REFUSE:
                    // 通知渠道
                    notifyChannel(merchantAccessDto);
                    process = false;
                    break;
                default:
                    log.info("进件流程处理器 当前状态无需处理 id:{},status:{}", id, accessStatus.getDesc());
            }
        }
        catch (Exception e) {
            log.error("当前进件异常 id:{}", id, e);
        }finally {
            rLock.forceUnlock();
            if (DateUtils.betweenDay(DateUtils.dateToString(merchantAccessInfoDO.getCreateTime(), DatePattern.NORM_DATETIME_PATTERN), DateUtils.getDateString(DatePattern.NORM_DATETIME_PATTERN)) < 1) {
                log.info("任务未超时继续执行 accessId:{} merchantAccessQuestType:{}", merchantAccessConsumerDto.getAccessId(), merchantAccessQuestType);
                if (process) {
                    nextProcessAccessMessage(merchantAccessConsumerDto, merchantAccessQuestType);
                }
            } else {
                log.info("任务超时停止执行 accessId:{} merchantAccessQuestType:{}", merchantAccessConsumerDto.getAccessId(), merchantAccessQuestType);
            }
        }
    }

    private void notifyChannel(MerchantAccessDto merchantAccessDto) {
        MerchantAccessInfoDO merchantAccessInfo = merchantAccessDto.getMerchantAccessInfo();
        if (!StringUtils.hasText(merchantAccessInfo.getNotifyUrl())) {
            log.info("没有填写回调的地址停止回调:{}", merchantAccessInfo.getMchOrderNo());
        }
        orderNotifyHandler.orderNotify(merchantAccessInfo.getRequestId(), 1, PayOrderEnums.ORDER_STATUS_TYPE.MERCHANT_ACCESS_STATUS);

    }

    public void nextProcessAccessMessage(MerchantAccessConsumerDto merchantAccessConsumerDto, Integer merchantAccessQuestType) {

        Long id = merchantAccessConsumerDto.getAccessId();
        log.info("nextProcessAccessMessage id:{}", id);
        MerchantAccessInfoDO merchantReportInfo = merchantAccessService.detail(id);
        MerchantEnum.ACCESS_STATUS accessStatus = MerchantEnum.ACCESS_STATUS.from(merchantReportInfo.getStatus());
        MerchantEnum.CHANNEL_CONFIG_STATUS channelConfigStatus = MerchantEnum.CHANNEL_CONFIG_STATUS.from(merchantReportInfo.getAuditStatus());
        MerchantEnum.MERCHANT_ACCESS_QUEST_TYPE reportQuestType;
        if (merchantAccessQuestType != null) {
            reportQuestType = MerchantEnum.MERCHANT_ACCESS_QUEST_TYPE.from(merchantAccessQuestType);
        }
        else {
            reportQuestType = MerchantEnum.MERCHANT_ACCESS_QUEST_TYPE.from(accessStatus, channelConfigStatus);
        }
        if (reportQuestType == null) {
            if (channelConfigStatus == null) {
                log.info("下一个流程无需进行状态处理,下一个流程进件状态为 id:{},accessStatus:{}", id, accessStatus.getDesc());
            }
            else {
                log.info("下一个流程无需进行状态处理,下一个流程进件状态为 id:{},accessStatus:{},channelConfigStatus:{}", id, accessStatus.getDesc(), channelConfigStatus.getDesc());
            }
            return;
        }
        else {
            log.info("下一个流程需要处理,下一个进件状态为 id:{},reportQuestType:{}", id, reportQuestType.getDesc());
        }
        boolean queryProcess = false;
        switch (reportQuestType) {
            case APPLY_QUERY:
            case SIGN_QUERY:
                queryProcess = true;
                break;
            case BUSINESS_CONFIG:
                switch (channelConfigStatus) {
                    case AUDIT:
                    case AUDITING:
                    case SIGN_QUERY:
                    case SUBMERINFO:
                    case WECHAT_CONFIGING:
                    case WECHAT_AUTHING:
                    case ALIPAY_AUTHING:
                        queryProcess = true;
                        break;
                    default:
                }
                break;
        }
        if (queryProcess) {
            sendMerchantAccessDelayMessage(merchantAccessConsumerDto);
        }
        else {
            sendMerchantAccessMessage(merchantAccessConsumerDto);
        }
    }

    public void sendMerchantAccessMessage(MerchantAccessConsumerDto merchantAccessConsumerDto) {
        String jsonString = JsonUtils.toJsonString(merchantAccessConsumerDto);
        log.info("sendMerchantAccessMessage:{}", jsonString);
        extRocketMQTemplate.send(MqConstant.MERCHANT_ACCESS_STATUS_TOPIC, jsonString);
    }

    public void sendMerchantAccessDelayMessage(MerchantAccessConsumerDto merchantAccessConsumerDto) {
        if (merchantAccessConsumerDto.getDelayLevel() == null) {
            merchantAccessConsumerDto.setDelayLevel(4);
        }
        else {
            // 延迟消息支持的时间 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
            if (merchantAccessConsumerDto.getDelayLevel() < 15) {
                merchantAccessConsumerDto.setDelayLevel(merchantAccessConsumerDto.getDelayLevel() + 1);
            }
        }
        merchantAccessConsumerDto.setDelaySendTime(System.currentTimeMillis());
        String jsonString = JsonUtils.toJsonString(merchantAccessConsumerDto);
        log.info("sendMerchantAccessDelayMessage:{}", jsonString);
        extRocketMQTemplate.sendDelay(merchantAccessConsumerDto.getDelayLevel(), MqConstant.MERCHANT_ACCESS_STATUS_TOPIC, jsonString);
    }
}
